#include "includes.h"

char *stat_str[8] = {"PARK_STAT_INIT", "PARK_STAT_START", "PARK_STAT_REQ", "PARK_STAT_RUN_INIT", "PARK_STAT_RUN", "PARK_STAT_PUSH", "PARK_STAT_UPDATA", "PARK_STAT_IDLE"};


int park_get_stat()
{
	
	return stPark.park_stat;
}

void park_set_stat(int stat)
{
	DBG("stat (%s) --> (%s) \n", stat_str[stPark.park_stat], stat_str[stat]);
	stPark.park_stat = stat;
}

void park_stat_backup()
{
	stPark.park_stat_old = stPark.park_stat;
}

void park_stat_reco()
{
	stPark.park_stat = stPark.park_stat_old;
}

void park_seq_req()
{
	stPark.seq_id++;
	stPark.seq_req = stPark.seq_id;
}

int park_seq_is_same(int seq)
{
	if (seq == stPark.seq_req)
		return 1;
	else
		return 0;
}

int park_seq_get()
{
	return (int)stPark.seq_id;
}

void park_seq_push_set(int seq)
{
	stPark.seq_ack = (unsigned short)seq;
}

int park_seq_push_get()
{
	return (int)stPark.seq_ack;
}
static unsigned long hash_count(void *buf)
{
	return *(unsigned long *)buf;
}

static int hash_compare(void *a, void *b)
{
	return memcmp(a, b, 4);
}

static int park_get_config_info()
{
	FILE *fp;
	char cmd[200];
	bzero(cmd, sizeof(cmd));

	fp = Fopen("/Skuo/DeviceInfo.ini", "r");
	while (fgets(cmd, sizeof(cmd), fp) != NULL) 
	{
		if (strstr(cmd, "ServerAddress"))
		{
			//printf("cmd %s\n", cmd);
			sscanf(cmd, "%*[^=]=%s", stPark.server_addr);
			DBG("server addr %s\n", stPark.server_addr);
		}
		else if (strstr(cmd, "ServerPort"))
		{
			//printf("cmd %s\n", cmd);
			sscanf(cmd, "%*[^=]=%d", &stPark.server_port);
			DBG("server port %d\n", stPark.server_port);
		}
	}

	fclose(fp);
	return 0;
}

static int park_area_count(PS_AREA pstArea)
{
	PS_PROBER pstProber;
	PS_SENSOR pstSensor;
	struct list_head *pstPos, *pstSubPos;
	//int i;
	pstArea->area_car_num_old = pstArea->area_car_num;
	pstArea->area_car_num = 0;
	list_for_each(pstPos, &pstArea->prober_list)
	{
		pstProber = list_entry(pstPos, S_PROBER, prober_list);
		if (pstProber->stat != PROBER_STAT_UPDATA)
			continue;

		list_for_each(pstSubPos, &pstProber->sensor_list)
		{
			pstSensor = list_entry(pstSubPos, S_SENSOR, sensor_list);
			{
				if (pstSensor->stat == SENSOR_STAT_NOCAR)
					pstArea->area_car_num++;
			}
		}

	}
	/*
	struct list_head *pstPosArea;
	PS_AREA pstArea
	if (stPark.pstArea == NULL)
	{
		DBG("stPark.pstArea == NULL\n");
		return;
	}
	list_for_each_a(pstPosArea, &stPark.pstArea->area_sys_list)
	{
		
	}
	*/
	return 0;
}

void* park_do_count(void *arg)
{
	PS_AREA pstArea;
	PS_LED pstLed;
	struct list_head *pstPos, *pstSubPos;
	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
	while(1)
	{
		list_for_each(pstPos, &stPark.area_list)
		{
			pstArea = list_entry(pstPos, S_AREA, area_sys_list);
			park_area_count(pstArea);
			if (pstArea->area_car_num_old != pstArea->area_car_num)
			{
				list_for_each(pstSubPos, &pstArea->led_list)
				{
					pstLed = list_entry(pstSubPos, S_LED, led_area_list);
					park_led_display(pstLed);
				}
			}
		}
		pthread_testcancel();
		sleep(1);
		pthread_testcancel();
	}
}

void park_do_count_first()
{
	PS_AREA pstArea;
	//PS_LED pstLed;
	struct list_head *pstPos;

	list_for_each(pstPos, &stPark.area_list)
	{
		pstArea = list_entry(pstPos, S_AREA, area_sys_list);
		park_area_count(pstArea);
		pstArea->area_car_num_old = pstArea->area_car_num;
	}
	return ;
}

void park_diplay_led_poll()
{
	PS_LED pstLed;
	struct list_head *pstPos;

	list_for_each(pstPos, &stPark.led_list)
	{
		pstLed = list_entry(pstPos, S_LED, led_list);
		if (strlen(pstLed->ip_addr) == 0)
			continue;
		printf("poll led %s port %d\n", pstLed->ip_addr, LED_PORT);
		park_led_display(pstLed);
	}
	return ;
}

#define add_off_dev_init() \
	do {\
		stPark.dev_offline_num = 0;\
		}while(0)

#define add_off_dev_list(x) \
	do {\
			if (stPark.dev_offline_num<MAX_OFFLINE_DEV_NUM)\
				stPark.dev_offline_list[stPark.dev_offline_num++] = (x);\
		}while(0)

static void park_do_sensor_poll(PS_PROBER pstProber)
{
	//int i;
	struct list_head *pstPos;
    PS_SENSOR pstSensor;

	if (pstProber == NULL)
	{
		DBG("pstProber == NULL !!!\n");
		return;
	}
	
	list_for_each(pstPos, &pstProber->sensor_list)
	{
		pstSensor = list_entry(pstPos, S_SENSOR, sensor_list);
		if (pstSensor->stat == SENSOR_STAT_OFFLINE)
		{
			DBG("sensor id (%d) off line!!\n", pstSensor->sensor_id);
			add_off_dev_list(pstSensor->sensor_id);
		}
	}
		
	return;
}

static void park_do_poll()
{
	struct list_head *pstPos, *pstSubPos;
	PS_AREA pstArea;
	PS_PROBER pstProber;
	//PS_LED pstLed;
	int ret;
	time_t cur_time;
	
	add_off_dev_init();
	list_for_each(pstPos, &stPark.area_list)
	{
		pstArea = list_entry(pstPos, S_AREA, area_sys_list);
		list_for_each(pstSubPos, &pstArea->prober_list)
		{
			pstProber = list_entry(pstSubPos, S_PROBER, prober_list);
			if (pstProber->stat == PROBER_STAT_REQ)
			{
				ret = park_prober_send_config_packet(pstProber);
				if (ret != 0)
				{
					pstProber->stat = PROBER_STAT_OFFLINE;
					add_off_dev_list(pstProber->prober_id);
				}
				else
				{
					pstProber->updata_time = time(NULL);
					pstProber->stat = PROBER_STAT_ONLINE;
				}
			}
			else if (pstProber->stat == PROBER_STAT_UPDATA)
			{
				cur_time = time(NULL);
				if (cur_time - pstProber->updata_time < MIN_PROBER_STAT_TIME)
				{
					park_do_sensor_poll(pstProber);
				}
				else
				{
					ret = park_prober_is_online(pstProber);
					if (ret != 0)
					{
						pstProber->stat = PROBER_STAT_OFFLINE;
						add_off_dev_list(pstProber->prober_id);
						DBG("prober id (%d) off line!!\n", pstProber->area_id);
					}
					else
					{
						park_do_sensor_poll(pstProber);
					}
				}
			}
			else if (pstProber->stat == PROBER_STAT_OFFLINE)
			{
				ret = park_prober_send_config_packet(pstProber);
				if (ret == 0)
				{
					pstProber->stat = PROBER_STAT_ONLINE;
					pstProber->updata_time = time(NULL);
					DBG("prober id (%d) on line!!\n", pstProber->area_id);
				}
				else
				{
					add_off_dev_list(pstProber->prober_id);
				}
			}
			else if (pstProber->stat == PROBER_STAT_ONLINE)
			{
				cur_time = time(NULL);
				if (cur_time - pstProber->updata_time > MAX_PROBER_STAT_TIME)
				{
					DBG("ONLINE stat timeout!!\n");
					pstProber->stat = PROBER_STAT_OFFLINE;
					add_off_dev_list(pstProber->prober_id);
				}
			}
		}
	}

	return;
}

void* park_stat_control(void *arg)
{
	//int port;

	int ret;
	int cnt;
	
	struct timespec ts;
	park_set_stat(PARK_STAT_INIT);
	while(1)
	{
		switch(park_get_stat())
		{
			case PARK_STAT_INIT:
				bzero(&stPark, sizeof(S_PARK_INFO));
				//park_set_stat(PARK_STAT_INIT);
				//Pthread_mutex_init(stPark.park_lock, NULL);
				park_get_config_info();
				INIT_LIST_HEAD(&stPark.floor_list);
				INIT_LIST_HEAD(&stPark.led_list);
				INIT_LIST_HEAD(&stPark.area_list);
				Sem_init(&stPark.park_sem, 0, 0);
				//stPark.pstHash = hash_create(0,unsigned int size,hash_count_cp count_cp,hash_compare_cp compare_cp,hash_copy_key_cp copy_key_cp,hash_copy_value_cp copy_value_cp)
				stPark.dev_offline_list = (int *)Malloc(MAX_OFFLINE_DEV_NUM*sizeof(int));				
				park_commu_init();
				park_set_stat(PARK_STAT_REQ);
				break;
				
			case PARK_STAT_REQ:
				ret = park_commu_send_requst_packet();
				if (ret != 0)
				{
					sleep(5);
					park_set_stat(PARK_STAT_REQ);
				}
				else
				{
					park_stat_backup();
					park_set_stat(PARK_STAT_IDLE);
				}
				break;
				
			case PARK_STAT_START:
				stPark.pstHash = hash_create(0, PARK_HASH_SIZE, hash_count, hash_compare, NULL, NULL);
				if (stPark.pstHash == NULL)
				{
					ERR_SYS("hash_create error");
				}
				park_commu_decode(stPark.pstMxmlTmpNode);
				mxmlDelete(stPark.pstMxmlTopNode);
				park_set_stat(PARK_STAT_RUN_INIT);
				break;

			case PARK_STAT_RUN_INIT:
				park_prober_init();
				park_prober_send_req();
				cnt = 50;
				while(cnt--)
				{
					if (park_prober_scan() == 0)
					{
						break;
					}
					sleep(1);
				}
				park_prober_modify_stat();
				park_do_count_first();
				park_diplay_led_poll();
				park_set_stat(PARK_STAT_RUN);
				break;
				
			case PARK_STAT_RUN:
				park_do_poll();
				ret = pthread_kill(stPark.pthread_id, 0);
				if (ret!=0 && ret==ESRCH)
				{
					Pthread_create(&stPark.pthread_id, NULL, park_do_count, NULL);
				}
				park_stat_backup();
				park_set_stat(PARK_STAT_IDLE);
				break;
				
			case PARK_STAT_PUSH:
				pthread_cancel(stPark.pthread_id);
				pthread_join(stPark.pthread_id, NULL);
				park_commu_free();
				hash_destroy(stPark.pstHash);
				park_prober_destroy();
				park_set_stat(PARK_STAT_START);
				break;
				
			case PARK_STAT_IDLE:
				ts.tv_sec = time(NULL)+PARK_STAT_IDLE_TIMEOUT;
				ts.tv_nsec = 0;
				while((ret=sem_timedwait(&stPark.park_sem, &ts))==-1 && errno==EINTR)
					continue;
				if (ret == -1)
				{
					if (errno == ETIMEDOUT)
					{
						park_stat_reco();
					}
					else
					{
						ERR_SYS("sem_timedwait error");
					}
				}
				break;				
		}
	}
}

int main(void)
{
	park_stat_control(NULL);
	return 0;
}
